package com.lanchetech.im.config;

import com.alibaba.fastjson.JSON;
import com.lanchetech.bean.request.UserOnlineReq;
import com.lanchetech.bean.response.BaseResp;
import com.lanchetech.bean.response.ResultData;
import com.lanchetech.bean.response.UserOnlineResp;
import com.lanchetech.bean.vo.MessageVO;
import com.lanchetech.common.enums.ResultCode;
import com.lanchetech.im.service.ImService;
import io.netty.handler.codec.http.HttpHeaders;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.yeauty.annotation.*;
import org.yeauty.pojo.Session;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


@ServerEndpoint(value = "/im/{eventType}", port = "${ws.port}")
@Component
public class WebSocketServer {

    public static ImService imService;

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
     */
    private static int onlineCount = 0;
    /**
     * concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
     */
    private static ConcurrentHashMap<Long, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    /**
     * 与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    private Session session;
    /**
     * 接收userId
     */
    private Long userId = null;

    private static String getMatcher(String regex, String source) {
        String result = "";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(source);
        while (matcher.find()) {
            result = matcher.group(1);//只取第一组
        }
        return result;
    }

    @BeforeHandshake
    public void handshake(Session session, HttpHeaders headers, @RequestParam String req, @RequestParam MultiValueMap reqMap, @PathVariable String arg, @PathVariable Map pathMap){
        session.setSubprotocols("stomp");
        String token = reqMap.getFirst("token").toString();
        if (!imService.onUserLoginVerify(token)){
            log.info("登录失败!");
            session.close();
        }
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, HttpHeaders headers, @RequestParam String req, @RequestParam MultiValueMap reqMap, @PathVariable String arg, @PathVariable Map pathMap) {
        this.session = session;
        String token = reqMap.size() > 0 ? reqMap.getFirst("token").toString() : headers.get("authorization");

        this.userId = imService.getUserId(token);
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId);
            webSocketMap.put(userId, this);
            //加入set中
        } else {
            webSocketMap.put(userId, this);
            //加入set中
            addOnlineCount();
            //在线数加1
        }
        imService.imRouter(userId, pathMap.get("eventType").toString(), webSocketMap, session);
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId);
            //从set中删除
            subOnlineCount();
        }
        log.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(Session session, String message) {
        //消息保存到数据库、redis
        if (StringUtils.isNotBlank(message)) {
            try {
                if (message.equals("ping")) {
                    // 如果还在map中，才继续发pong，否则就停止发pong，让客户端断掉重新连接
                    if (webSocketMap.containsKey(userId)) {
                        session.sendText("pong");
                    }
                } else {
                    log.debug(JSON.toJSONString(message));
                    //解析发送的报文
                    imService.messageRouter(userId, message, webSocketMap, session);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误:" + userId + ",原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.sendText(message);
    }

    /**
     * 实现服务器主动推送
     */
    public static BaseResp sendToUser(Long userId, String type, String message) throws IOException {
        if (webSocketMap.containsKey(userId)) {
            MessageVO vo = new MessageVO(type, message);
            webSocketMap.get(userId).sendMessage(JSON.toJSONString(vo));
            return new BaseResp();
        }else{
            System.out.println("用户未在线" +  userId.toString());
            return new BaseResp(ResultCode.ANCHOR_NOT_ONLINE);
        }
    }

    /**
     * 发送自定义消息
     */
//    public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
//        log.info("发送消息到:"+userId+"，报文:"+message);
//        if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
//            webSocketMap.get(userId).sendMessage(message);
//        }else{
//            log.error("用户"+userId+",不在线！");
//        }
//    }
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }

    /**
     * 用户是否在线
     * @param req
     * @return
     */
    public static ResultData<List<UserOnlineResp>> isUserOnline(UserOnlineReq req) {
        List<UserOnlineResp> list = new ArrayList<>();
        if(!CollectionUtils.isEmpty(req.getMsgUserIds())){
            req.getMsgUserIds().stream().forEach(item->{
                list.add(UserOnlineResp.builder()
                        .msgUserIds(item)
                        .isOnline(webSocketMap.containsKey(item))
                        .build());
            });
        }
        return new ResultData<>(list);
    }
}